/**
 * ErgoDOX RGB Backlight test
 *
 * Copyright 2013 Jarl Stefansson
 * This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/** Documentation for LM3549 **
 * 
 * Save Config
 * -----------
 * To store current register values to EEPROM user needs to first write 04h to register 40h (EE init bit to “1”) followed by 02h to register 40h (EE prog bit to “1”).
 * Once EEPROM programming is completed LM3549 sets EE_ready bit to 1
 * EEPROM programming should always be done in standby mode
 * 
 * Set Current
 * -----------
 * There are three 10 bit current settings for each driver. 10 bits are divided into two eight bit registers. First register
 * holds the eight least significant bits (LSB) and the second register holds the two most significant bits (MSB).
 * These settings are grouped into three banks. IR0, IG0 and IB0 form a bank0; IR1, IG1 and IB1 form a bank1 and
 * IR2, IG2 and IB2 form a bank2. For example IR0_MSB holds the two MSB for red on bank0 and IR0_LSB the
 * eight LSB for red on bank0. Bank is selected with BANK_SEL register (00 = bank0, 01 = bank1 and 10 or 11 = bank2).
 * 
 * For currents up to 550 mA current setting can be calculated using formula
 * ISET = (target current in mA - 100 mA)/(650mA/1024)
 * 
 * For currents between 550mA and 700mA current setting can be calculated using formula:
 * ISET = (target current in mA - 550 mA)/0.479 mA + 710
 * 
 * Set Brightness
 * --------------
 * In case of PWM input brightness control (BRC) is the positive duty cycle of the input signal.
 * In case of FADER register brightness is MASTER FADER[7:0]/255
 * 
 * MFE  PWM  Brightness Control
 * 0    0    No brightness control
 * 0    1    PWM input
 * 1    0    FADER register
 * 1    1    PWM input
 * 
 * Brightness control keeps the ratio of
 * the driver currents constant and adjusts the output currents based on the highest current setting. Driver currents
 * can be adjusted between 100 mA to the maximum current set in the registers
 * 
 * ISET1 =highest current setting
 * ISET2 =current setting 2
 * ISET3 =current setting 3
 * R1 =(ISET2/ISET1), ratio of current 2 and the highest current
 * R2 =(ISET3/ISET1), ratio of current 3 and the highest current
 * BRC =brightness control
 * I1 = ISET1 x BRC)
 * I2 = I1 x R1
 * I3 = I1x R2
 * 
 * To ensure that control takes effect for the next color time from PWM
 * change to next enable needs to be greater than timeout time (300 µs typical)
 * 
 * 
 * Fault Detection
 * ---------------
 * 
 * LED OPEN FAULT
 * Open fault is generated when at the end of color VOUT is at maximum and no current is flowing through driver
 * (VDx = 0V). Also OCP fault needs to be low. Open fault can be generated by broken LED or a soldering defect.
 * 
 * LED SHORT FAULT
 * Short fault is detected when VOUT < 1.0V at the end of a color. Short fault is generated when VOUT is shorted
 * to driver by soldering defect or faulty LED. Driver current limit limits the maximum current. Depending on output
 * current and positive limit settings, LED short can also generate OCP fault to fault register.
 * 
 * TSD FAULT
 * Thermal shutdown (TSD) fault is generated if junction temperature rises above TSD level. TSD engages at TJ=
 * 150°C (typ) and disengages at TJ = 140°C (typ). TSD sets device to standby mode. Occasionally a false TSD
 * fault is generated to Fault register when device goes from shutdown mode to standby mode. It is good practice to
 * reset the fault register by reading it every time after device is set from shutdown mode to standby mode.
 * 
 * UVLO FAULT
 * Under voltage lock out (UVLO) fault is generated if VIN drops below UVLO level (~2.5V). UVLO sets device to
 * standby mode. When VIN rises back above the 2.5V device exits UVLO. If control register values were changed
 * from EEPROM defaults they need to be rewritten to registers because UVLO condition can generate EEPROM
 * read sequence.
 * 
 * OVER CURRENT PROTECTION FAULT
 * Over current protection (OCP) fault is generated when positive current limit is active at the end of a color. It is
 * important to notice that OCP fault is not always set when positive current limit is activated. Positive current limit
 * can activate during normal operation when buck-boost is adjusting the output voltage to a higher level. OCP can
 * be caused by short from VOUT to GND, short from driver to GND or if too low positive current limit value is set
 * for desired output current.
 * 
 * I2C Register Map
 * ADDR NAME      D7      D6      D5      D4      D3      D2       D1        D0       DEFAULT     NOTE
 * ----|--------|-----|------|--------|--------|-------|-------|--------|--------|-------------|--------
 * 00H  BANK_SEL  -------------------------------------------- |   Bank_sel[1:0] |    00H         EEPROM
 * 01H  IR0_LSB   ----------------------- Red 0 [7:0] -------------------------- |    81H         EEPROM
 * 02H  IR0_MSB   ------------------- N/A -------------------- |   Red 0 [9:8]   |    01H         EEPROM
 * 03H  IG0_LSB   ----------------------- Green 0 [7:0] -------------------------|    81H         EEPROM
 * 04H  IG0_MSB   ------------------- N/A ---------------------|   Green 0 [9:8] |    01H         EEPROM
 * 05H  IB0_LSB   ----------------------- Blue 0 [7:0] --------------------------|    81H         EEPROM
 * 06H  IB0_MSB   ------------------- N/A ---------------------|   Blue 0 [9:8]  |    01H         EEPROM
 * 07H  IR1_LSB   ----------------------- Red 1 [7:0] ---------------------------|    E7H         EEPROM
 * 08H  IR1_MSB   ------------------- N/A ---------------------|   Red 1 [9:8]   |    00H         EEPROM
 * 09H  IG1_LSB   ----------------------- Green 1 [7:0] -------------------------|    E7H         EEPROM
 * 0AH  IG1_MSB   ------------------- N/A ---------------------|   Green 1 [9:8] |    00H         EEPROM
 * 0BH  IB1_LSB   ----------------------- Blue 1 [7:0]  -------------------------|    E7H         EEPROM
 * 0CH  IB1_MSB   ------------------- N/A ---------------------|   Blue 1 [9:8]  |    00H         EEPROM
 * 0DH  IR2_LSB   ----------------------- Red 2 [7:0] ---------------------------|    4DH         EEPROM
 * 0EH  IR2_MSB   ------------------- N/A ---------------------|   Red 2 [9:8]   |    00H         EEPROM
 * 0FH  IG2_LSB   ----------------------- Green 2 [7:0] -------------------------|    4DH         EEPROM
 * 10H  IG2_MSB   ------------------- N/A ---------------------|   Green 2 [9:8] |    00H         EEPROM
 * 11H  IB2_LSB   ----------------------- Blue 2 [7:0] --------------------------|    4DH         EEPROM
 * 12H  IB2_MSB   ------------------- N/A ---------------------|   Blue 2 [9:8]  |    00H         EEPROM
 * 13H  FADER     ----------------------- MASTER FADER [7:0] --------------------|    FFH         EEPROM
 * 14H  CTRL      --- N/A ---| SOFT START[1:0] | TIME OUT[1:0] |  MFE   |   PWM  |    00H         EEPROM
 * 15H  ILIMIT    --- N/A ---| POS_LIMIT[1:0]  |   N/A         |  NEG_LIMIT[1:0] |    11H         EEPROM
 * 16H  F_MASK    -------- N/A -------| SHORT  | OPEN  | UVLO  |  TSD   |   OCP  |    00H         EEPROM
 * 17H  FAULT     N/A | SHORT[1:0]    |    OPEN[1:0]   | UVLO  |  TSD   |   OCP  |    00H         Read Only
 * 19H  USR1      ----------------------- User Register1[7:0] -------------------|    00H         EEPROM
 * 1AH  USR2      ------------------------User Register2[7:0] -------------------|    00H         EEPROM
 * 40H  EEPROM    EE  | -------------------------------|  EE   |   EE   |   EE   |    00H         R/W  
 * CONTROL  ready| -------------------------------| init  |  prog  |  read  |
 * ----|--------|-----|------|--------|--------|-------|-------|--------|--------|-------------|--------
 * ADDR NAME      D7      D6      D5      D4      D3      D2       D1        D0       DEFAULT     NOTE
 * 
 * 
 * 
 * I2C Register Details
 * --------------------
 * 
 * 00h BANK_SEL[1:0]			-	Bank selection register. Selects one of the three current setting banks.
 * 
 * 	BIT   BANK  SELECTION
 * 	0     0     Bank 0
 * 	0     1     Bank 1
 * 	1     0     Bank 2
 * 	1     1     Bank 2
 * 
 * 01h IR0_LSB and 02h IR0_MSB  -  Red LED current setting for Bank 0. IR0_LSB holds the eight least significant bits and IR0_MSB the two mostsignificant bits.
 * 03h IG0_LSB and 04h IG0_MSB  -  Green LED current setting for Bank 0. IG0_LSB holds the eight least significant bits and IG0_MSB the two most significant bits.
 * 05h IB0_LSB and 06h IB0_MSB  -  Blue LED current setting for Bank 0. IB0_LSB holds the eight least significant bits and IB0_MSB the two most significant bits.
 * 07h IR1_LSB and 08h IR1_MSB  -  Red LED current setting for Bank 1. IR1_LSB holds the eight least significant bits and IR1_MSB the two most significant bits.
 * 09h IG1_LSB and 0Ah IG1_MSB  -  Green LED current setting for Bank 1. IG1_LSB holds the eight least significant bits and IG1_MSB the two most significant bits.
 * 0Bh IB1_LSB and 0Ch IB1_MSB  -  Blue LED current setting for Bank 1. IB1_LSB holds the eight least significant bits and IB1_MSB the two most significant bits.
 * 0Dh IR2_LSB and 0Eh IR2_MSB  -  Red LED current setting for Bank 2. IR2_LSB holds the eight least significant bits and IR2_MSB the two most significant bits.
 * 0Fh IG2_LSB and 10h IG2_MSB  -  Green LED current setting for Bank 2. IG2_LSB holds the eight least significant bits and IG2_MSB the two most significant bits.
 * 11h IB2_LSB and 12h IB2_MSB  -  Blue LED current setting for Bank 2. IB2_LSB holds the eight least significant bits and IB2_MSB the two most significant bits.
 * 13h FADER                    -  Master fader control register. Can be used to control the total brightness of the LEDs if MFE is enabled.
 * 14h CTRL                     -  Control register. Controls many of the LM3549 features.
 * 	BIT[1:0] PWM and MFE         - Control register bits [1:0] can be used to enable master control or PWM brightness control.
 * 		MFE   PWM   BRIGHTNESS CONTROL
 * 		0     0     No brightness control
 * 		0     1     PWM input
 * 		1     0     Master input
 * 		1     1     PWM input
 * 	BIT[3:2] TIME OUT[1:0]       - Selects how long device stays in active mode after all x_EN controls have been set low
 * 		BIT   TIME OUT
 * 		0 0   125 ms
 * 		0 1   250 ms
 * 		1 0   500 ms
 * 		1 1   1s
 * 	BIT[5:4] SOFT START[1:0]     - Enables soft start feature and selects soft start time.
 * 		BIT   SOFT START TIME
 * 		0 0   disabled
 * 		0 1   0.5s
 * 		1 0   1s
 * 		1 1   2s
 * 15h ILIMIT                    - ILIMIT register sets the buck-boost converters current limit values.
 * 	BIT[1:0] NEG_LIMIT[1:0]       - Selects buck-boost converters negative current limit.
 * 		BIT   NEGATIVE CURRENT LIMIT
 * 		0 0   550 mA
 * 		0 1   1100 mA
 * 		1 0   1650 mA
 * 		1 1   2200 mA
 * 	BIT[5:4] POS_LIMIT[1:0]      - Selects buck-boost converters positive current limit.
 * 		BIT   POSITIVE CURRENT LIMIT
 * 		0 0   500 mA
 * 		0 1   1000 mA
 * 		1 0   1500 mA
 * 		1 1   2000 mA
 * 16h F_MASK                  - Fault output mask register. Can be used to disable fault output from desired faults.
 * 17h FAULT                   - Fault register. If fault occurs corresponding fault bits are set in fault register. Reading Fault register resets it. Read only register.
 * 	BIT[0] OCP                   - Over current protection. Buck-boost converters current limit has been reached.
 * 	BIT[1] TSD                   - Thermal shutdown fault. Junction temperature has risen abowe TSD level.
 * 	BIT[2] UVLO                  - Under voltage lock-out. Input voltage has fallen below UVLO threshold level.
 * 	BIT[4:3] OPEN[1:0]           - LED open fault.
 * 		BIT   FAULT
 * 		0 0   No fault
 * 		0 1   Red open
 * 		1 0   Green open
 * 		1 1   Blue open
 * 	BIT[6:5] SHORT[1:0]          - LED short fault.
 * 		BIT   FAULT
 * 		0 0   No fault
 * 		0 1   Red short
 * 		1 0   Green short
 * 		1 1   Blue short
 * 19h and 1AH USR1 and USR2   - User registers 1 and 2. Can be used to store any user data. No affect on the device.
 * 40h EEPROM CONTROL          - EEPROM Control register. This register is used to program EEPROM.
 */

// The I2C library support Repeated Starts unlike the Wire library which is bugged.
// http://dsscircuits.com/articles/arduino-i2c-master-library.html
#include "I2C.h"

// Set RGB LED enable pins
const int BlueLedPin = 10;
//const int GreenLedPin = 11;
const int GreenLedPin = 22;
const int RedLedPin = 12;

// Define Register Addresses for LM3549 LED Driver
#define BANK_SEL_REG      0x00
#define IR0_LSB_REG       0x01
#define IR0_MSB_REG       0x02
#define IG0_LSB_REG       0x03
#define IG0_MSB_REG       0x04
#define IB0_LSB_REG       0x05
#define IB0_MSB_REG       0x06
#define IR1_LSB_REG       0x07
#define IR1_MSB_REG       0x08
#define IG1_LSB_REG       0x09
#define IG1_MSB_REG       0x0A
#define IB1_LSB_REG       0x0B
#define IB1_MSB_REG       0x0C
#define IR2_LSB_REG       0x0D
#define IR2_MSB_REG       0x0E
#define IG2_LSB_REG       0x0F
#define IG2_MSB_REG       0x10
#define IB2_LSB_REG       0x11
#define IB2_MSB_REG       0x12
#define FADER_REG         0x13
#define CTRL_REG          0x14
#define ILIMIT_REG        0x15
#define FMASK_REG         0x16
#define FAULT_REG         0x17
#define USR1_REG          0x19
#define USR2_REG          0x1A
#define EEPROM_REG        0x40

// Define Parameters and arguments when talking to LM3549 LED Driver
#define RED    0x00
#define GREEN  0x01
#define BLUE   0x02

#define NO_BRIGHTNESS_CTRL   0x00
#define PWM_BRIGHTNESS_CTRL  0x01
#define MFE_BRIGHTNESS_CTRL  0x02

#define TIME_OUT_125MS       0x00
#define TIME_OUT_250MS       0x01
#define TIME_OUT_500MS       0x02
#define TIME_OUT_1S          0x03

#define SOFT_START_OFF      0x00
#define SOFT_START_500ms    0x01
#define SOFT_START_1s       0x02
#define SOFT_START_2s       0x03

// Fault codes
#define OCP_FAULT           0b00000001
#define TSD_FAULT           0x00000010
#define UVLO_FAULT          0x00000100
#define OPEN_FAULT          0x00001000
#define SHORT_FAULT         0x00010000
#define RED_SHORT_FAULT     0x000|01|000
#define GREEN_SHORT_FAULT   0x000|10|000
#define BLUE_SHORT_FAULT    0x000|11|000
#define RED_OPEN_FAULT      0x0|01|00000
#define GREEN_OPEN_FAULT    0x0|10|00000
#define BLUE_OPEN_FAULT     0x0|11|00000

// USB 2.0 has a max current limit of 500mA, hence we hardset converters to 500/550mA
#define ILIMIT_4USB         0x00

uint8_t LM3549_ADDR = 0x36;

// Selects one of the three current setting banks
//select_bank  (0,1,2)

// Calculate offsets for bank/color, returns first register, e.g. least significant bits
uint8_t lookup_reg_addr(uint8_t color, uint8_t bank) {
  return (bank * 6) + ((color * 2) + 1);
}

// Sets LED current for a color on the specified bank, current specified in milliAmps, max 700mA
uint8_t set_current(uint16_t current, uint8_t color, uint8_t bank = 0) {
  // Look up registers for bank/color
  uint8_t lsb_register = lookup_reg_addr(color, bank);
  uint8_t msb_register = lookup_reg_addr(color, bank)+1;
  uint16_t target_current;
  float low_current_ratio = 0.635; // (650 / 1024)
  float high_current_ratio = 0.479;
  // Current is specified using 10 bits or 1024 possible values, values are linear up to 550mA, then tails off
  // Minimum current output is 100mA
  if (current < 550) {
    target_current = (current - 100) / low_current_ratio; // Correct up to 550mA
  } 
  else {
    target_current = ((current - 550) / high_current_ratio) + 710; // Correct from 550mA to 700mA

  }
  uint8_t msb_value = (uint8_t)(target_current >> 8);
  uint8_t lsb_value = (uint8_t)(target_current);
  Serial.print("Setting Current to ");
  Serial.print(current);
  Serial.print("mA equal to ");
  Serial.print(target_current);
  Serial.print(" out of 1024 by setting MSB register to 0x");
  Serial.print(msb_register, HEX);
  Serial.print(" to 0x" );
  Serial.print(msb_value, HEX);
  Serial.print(" and LSB register 0x" );
  Serial.print(lsb_register, HEX);
  Serial.print(" to 0x" );
  Serial.println(lsb_value, HEX); 

  I2c.write(LM3549_ADDR, lsb_register, lsb_value);
  I2c.write(LM3549_ADDR, msb_register, msb_value);
}
// Get LED current for a color on a bank
uint16_t get_current (uint8_t color, uint8_t bank = 0) {
  uint8_t lsb_register = lookup_reg_addr(color, bank);
  uint8_t msb_register = lookup_reg_addr(color, bank)+1;
  // Read registers from LED driver and combine them into one 16 bit number
  Serial.print("Reading MSB from register 0x");
  Serial.print(msb_register, HEX);
  I2c.read(LM3549_ADDR, lsb_register, (uint8_t)1);
  uint8_t current_lsb = I2c.receive();
  I2c.read(LM3549_ADDR, msb_register, (uint8_t)1);
  uint8_t current_msb = I2c.receive();
  Serial.print(" which returned 0x");
  Serial.print(current_msb, HEX);
  Serial.print(" and LSB from register 0x");
  Serial.print(lsb_register, HEX);
  Serial.print(" which returned 0x");
  Serial.println(current_lsb, HEX);
  uint16_t current = ((uint16_t) current_msb << 8) | current_lsb ;
  Serial.print("Current is set to ");
  Serial.print(current);
  Serial.println(" out of 1024.");
  return current;
}

// Sets the (M)aster (F)ader (C)ontrol, controls brighness of all LEDs
//set_brightness (0-15)

// Selects if brighness is controlled and if it is by MFC or PWM
//select_brighness_control (0,1,2)

// Selects how long device stays in active mode after all (EN)able controls have been set low
//set_time_out (0,1,2,3)

// Enables soft start feature and selects soft start time.
//set_soft_start

// Set the buck-boost converters current limit values.

// Acknowledge faults, used to disable fault output from desired faults
//ack_fault (0,1,2,3,4)

// Get fault codes from LED Driver
//get_faults

// Save current config to EEPROM

// Restore EEPROM factory defaults

// the setup() method runs once, when the sketch starts

void setup() {
  // Configure and set up Serial Communications
  Serial.begin(19200);

  // Configure and set up I2C
  I2c.pullup(0);
  I2c.timeOut(5000);
  I2c.begin();

  // Find addresses for all I2C peripherals, figure out types and re-address any duplicates
  //I2c.scan();


  // initialize the digital pin as an output.
  //    I2c.write(LM3549_ADDR, 0x00, 0x00);
  //  I2c.write(0x36, 0x01, 0x00);
  //  I2c.write(0x36, 0x02, 0x00);
  //  I2c.write(0x36, 0x03, 0x00);
  //  I2c.write(0x36, 0x04, 0x00);
  //  I2c.write(0x36, 0x05, 0x00);
  //  I2c.write(0x36, 0x06, 0x00);
  //  I2c.write(0x36, 0x07, 0x00);
  //  I2c.write(0x36, 0x06, 0x00);
  //  I2c.write(0x36, 0x08, 0x00);
  //  I2c.write(0x36, 0x09, 0x00);
  //  I2c.write(0x36, 0x0A, 0x00);
  //  I2c.write(0x36, 0x0B, 0x00);
  //  I2c.write(0x36, 0x0C, 0x00);
  //  I2c.write(0x36, 0x0D, 0x00);
  //  I2c.write(0x36, 0x0E, 0x00);
  //  I2c.write(0x36, 0x0F, 0x00);
  //  I2c.write(0x36, 0x10, 0x00);
  //  I2c.write(0x36, 0x11, 0x00);
  //  I2c.write(0x36, 0x12, 0x00);

  pinMode(RedLedPin, OUTPUT);
  pinMode(GreenLedPin, OUTPUT);
  pinMode(BlueLedPin, OUTPUT);
  digitalWrite(RedLedPin, LOW);   // set the LED on
  digitalWrite(GreenLedPin, LOW);   // set the LED on
  digitalWrite(BlueLedPin, LOW);   // set the LED on
  //Serial.begin(9600);      // open the serial port at 9600 bps:

  //    for (int thisPin = 1; thisPin < 23; thisPin++)  {
  //      pinMode(thisPin, OUTPUT);
  //      digitalWrite(thisPin, LOW);
  //    }
  //    digitalWrite( 10, HIGH);
}

// the loop() methor runs over and over again,
// as long as the board has power

byte num;
void loop() {
  //  for (int cur_reg = 0; cur_reg < 127; cur_reg++)  {
  //    int cur_reg = 0x17;
  //    Serial.print(cur_reg, HEX);
  //    Serial.print(" -> ");
  //    I2c.read(0x36, 0x05, 1);
  //    Serial.println(I2c.receive(), HEX);    
  //    I2c.read(0x36, 0x06, 1);
  //    Serial.println(I2c.receive(), HEX);    
  //    delay(100);
  //    digitalWrite(BlueLedPin, HIGH);   // set the LED on
  //    delay(1000);
  //    digitalWrite(BlueLedPin, LOW);   // set the LED on
  //    digitalWrite(GreenLedPin, HIGH);   // set the LED on
  //    delay(100);
  //    digitalWrite(GreenLedPin, LOW);   // set the LED on
  //    digitalWrite(RedLedPin, HIGH);   // set the LED on
  //    delay(100);
  //    digitalWrite(RedLedPin, LOW);   // set the LED on
  //  }
  //  set_current(250, BLUE, 0);
  //  set_current(250, RED, 0);
  //  set_current(250, GREEN, 0);
  //    digitalWrite(BlueLedPin, HIGH);   // set the LED on
  //    delay(1000);
//  get_current(BLUE);
//  digitalWrite(BlueLedPin, HIGH);   // set the LED on
//  delay(3000);
//  digitalWrite(BlueLedPin, LOW);
  //  set_current(100, RED, 0);
  //  set_current(100, GREEN, 0);
//  digitalWrite(BlueLedPin, HIGH);
//  delay(3000);
//  digitalWrite(BlueLedPin, LOW);
  set_current(250, BLUE);
  analogWrite(BlueLedPin, 255);
  get_current(BLUE);
  delay(3000);  
  analogWrite(BlueLedPin, 0);
  delay(2000);
  set_current(250, GREEN);
  analogWrite(GreenLedPin, 255);
  get_current(GREEN);
  delay(3000);  
  analogWrite(GreenLedPin, 0);
  delay(2000);
  set_current(250, RED);
  analogWrite(RedLedPin, 255);
  get_current(RED);
  delay(3000);  
  analogWrite(RedLedPin, 0);
  delay(2000);
  
//  set_current(110, BLUE);
//    analogWrite(BlueLedPin, 10);
//  delay(3000);
//  analogWrite(BlueLedPin, 0);
//  get_current(BLUE);
//  delay(3000);
}



